
#if defined(WIN32) || defined(_WIN32)
#define CYBERTRON_WIN 1
#include <asio/asio.hpp>
#include <windows.h>
#endif

#include "cybertron/network/Message.hpp"
#include "cybertron/core/Log.hpp"
#include <asio.hpp>
#include <google/protobuf/message.h>
#include <string.h>

CYBERTRON_BEGIN

MessageBlock::MessageBlock() {
  ref = 0;
  length = 6;
  memset(data, 0, sizeof(data));
}

MessageBlock &dummyMessage() {
  static MessageBlock sDummy;
  return sDummy;
}

CybertronCoreAPI Message::Message() { mpPointer = &dummyMessage(); }

CybertronCoreAPI Message::Message(const Message &rhs) {
  if (rhs.mpPointer == &dummyMessage()) {
    mpPointer = rhs.mpPointer;
    return;
  }

  rhs.mpPointer->ref++;
  mpPointer = rhs.mpPointer;
}

CybertronCoreAPI Message::Message(Message &&rhs) {
  mpPointer = rhs.mpPointer;
  rhs.mpPointer = &dummyMessage();
}

CybertronCoreAPI Message &Message::operator=(const Message &rhs) {
  Message temp(rhs);
  swap(temp);

  return *this;
}

CybertronCoreAPI Message &Message::operator=(Message &&rhs) {
  clear();

  mpPointer = rhs.mpPointer;
  rhs.mpPointer = &dummyMessage();

  return *this;
}

CybertronCoreAPI Message::~Message() { clear(); }

CybertronCoreAPI void Message::swap(Message &rhs) {
  std::swap(mpPointer, rhs.mpPointer);
}

CybertronCoreAPI void Message::clear() {
  if (mpPointer == &dummyMessage()) {
    return;
  }

  if (mpPointer != nullptr)
  {
	  if ((--mpPointer->ref) == 0) {
		  mpPointer->~MessageBlock();
		  delete[](unsigned char *) mpPointer;
	  }
  }

  mpPointer = &dummyMessage();
}

CybertronCoreAPI void Message::create(std::uint16_t msgId, size_t bodyLength) {
  clear();

  size_t totalLength = sizeof(MessageBlock) + bodyLength;
  unsigned char *pBuffer = new unsigned char[totalLength];
  new (pBuffer) MessageBlock();

  mpPointer = (MessageBlock *)pBuffer;
  mpPointer->length = bodyLength + HeaderSize;
  mpPointer->ref = 1;

  std::uint16_t networkMsgId = htons(msgId);
  std::uint32_t networkBodyLength = htonl((std::uint32_t)bodyLength);

  memcpy(mpPointer->data, &networkMsgId, sizeof(networkMsgId));
  memcpy(mpPointer->data + 2, &networkBodyLength, sizeof(networkBodyLength));
}

//CybertronCoreAPI void Message::createLog(std::string socketType, int logLevel,
//                                         std::string logContent) {
//  // pack
//  buf_t *buf = buf_new_line(512);
//  buf_write_str(buf, "");  // mSocketType
//  buf_write_i16l(buf, 11); // log level
//  buf_write_log(buf, "");  // log msg
//
//  // flush
//  clear();
//  size_t bodyLength = buf_data_len(buf); // bodyLength = 12+strLog.size();
//  size_t totalLength = sizeof(MessageBlock) + bodyLength;
//  unsigned char *pBuffer = new unsigned char[totalLength];
//  new (pBuffer) MessageBlock();
//
//  mpPointer = (MessageBlock *)pBuffer;
//  mpPointer->length = bodyLength + HeaderSize;
//  mpPointer->ref = 1;
//
//  std::uint16_t networkMsgId = htons(4003);
//  std::uint32_t networkBodyLength = htonl((std::uint32_t)bodyLength);
//
//  memcpy(mpPointer->data, &networkMsgId, sizeof(networkMsgId));
//  memcpy(mpPointer->data + 2, &networkBodyLength, sizeof(networkBodyLength));
//  memcpy(getDataBody(), buf_data(buf), buf_data_len(buf));
//
//  // free
//  buf_delete(buf);
//}

CybertronCoreAPI bool
Message::fromProtobuf(std::uint16_t msgId,
                      const google::protobuf::MessageLite &protoMsg) {
  try {
    size_t msgBodyLength = protoMsg.ByteSizeLong();
    create(msgId, msgBodyLength);
    protoMsg.SerializeToArray(getDataBody(), (int)msgBodyLength);
  } catch (std::exception& e) {
    logError("Protocol Buffer Serialization Error: %s", e.what());
    create(0, 0);
    return false;
  }

  return true;
}

CybertronCoreAPI bool
Message::fromString(const std::string &str)
{
    size_t msgBodyLength = str.length();
    create(0, msgBodyLength);
    unsigned char *dataBody = getDataBody();
    memcpy(dataBody, &str[0], msgBodyLength);

    return true;
}

CybertronCoreAPI bool
Message::toProtobuf(google::protobuf::MessageLite &protoMsg) const {
  try {
    int dataBodyLength = (int)getDataBodyLength();
    protoMsg.ParseFromArray(getDataBody(), dataBodyLength);
  } catch (std::exception& e) {
    logError("Protocol Buffer Deserialization Error: %s", e.what());
    return false;
  }

  return true;
}

CybertronCoreAPI std::uint16_t Message::parseMsgId() const {
  return ntohs(*((std::uint16_t *)mpPointer->data));
}

CybertronCoreAPI std::uint32_t Message::parseDataBodyLength() const {
  return ntohl(*((std::uint32_t *)(mpPointer->data + 2)));
}

CYBERTRON_END
